AnsibleによるVPC Peering
渡辺です。
今回は、VPC Peering関連のAnsibleのAWSモジュールを紹介します。
AWSではVPCがネットワークのベースとなり、VPCに幾つかのサブネットを作成し、リソースを配置することになります(一部、VPCと関連しないリソースもある)。 VPCは、システム単位に分割したり、検証環境と本番環境で分割したりと、複数作成するでしょう。 この時、原則としては各VPCは独立したネットワークであり、相互に通信を行うことができません。 このため、セキュアにネットワークを構成できるわけです。
一方、大規模システムで、複数のシステムが各VPCに構築されている場合、複数のAWSアカウントにシステムが分散されている場合など、VPC間の通信を許可したいケースもあります。 このような要求を満たすのが、VPC Peeringです。 VPC Peeringでは、複数のVPCの通信を許可します。 ただし、各VPCのCIDR Blockが重複していないことが前提です。
なお、今回利用するec2_vpc_peerモジュールは、Ansible2.2以降で利用可能なモジュールです。
VPC Peeringを行う場合のPlaybook
通常、Ansibleでは、環境(開発/検証/本番)毎にホストとグループ変数を利用して共通のPlaybookを利用すべきです。 これは重複を減らす事でメンテナンス性を高くしたいからです。 しかし、VPCピアリングが絡む場合、ひとつのPlaybookで可能な限りまとめましょう。
これは、VPCピアリングを行うと、VPC同士が相互参照することになるためです。 さらに、Routeテーブルなど、先にすべてのVPCとVPCピアリングの設定を済ませないと設定できないことがあります。 また、Ansibleでは依存関係の解決などは行われないため、どのリソースを先に作成するかも考慮します。
同一AWSアカウントでのVPC Peering
Playbookです。
--- - hosts: localhost connection: local gather_facts: False vars: profile: default region: us-east-2 vpc_a: name: VPC A cidr_blodk: 10.0.0.0/16 vpc_b: name: VPC B cidr_blodk: 10.1.0.0/16 tasks: - name: "{{ vpc_a.name }}" ec2_vpc_net: name: "{{ vpc_a.name }}" cidr_block: "{{ vpc_a.cidr_blodk }}" profile: "{{ profile }}" region: "{{ region }}" register: _vpc_a - debug: var=_vpc_a verbosity=1 - name: "{{ vpc_b.name }}" ec2_vpc_net: name: "{{ vpc_b.name }}" cidr_block: "{{ vpc_b.cidr_blodk }}" profile: "{{ profile }}" region: "{{ region }}" register: _vpc_b - debug: var=_vpc_b verbosity=1 # VPC Peering - name: "VPC Peering" ec2_vpc_peer: vpc_id: "{{ _vpc_a.vpc.id }}" peer_vpc_id: "{{ _vpc_b.vpc.id }}" profile: "{{ profile }}" region: "{{ region }}" register: _vpc_peer - debug: var=_vpc_peer verbosity=1 - name: "Accept VPC Peering request" ec2_vpc_peer: peering_id: "{{ _vpc_peer.peering_id }}" state: accept profile: "{{ profile }}" region: "{{ region }}" when: _vpc_peer.peering_id is defined - name: Public Route VPC A ec2_vpc_route_table: vpc_id: "{{ _vpc_a.vpc.id }}" tags: Name: "PublicRoute" routes: - dest: "{{ vpc_b.cidr_blodk }}" vpc_peering_connection_id: "{{ _vpc_peer.peering_id }}" profile: "{{ profile }}" region: "{{ region }}" when: - _vpc_peer.peering_id is defined - name: Public Route VPC B ec2_vpc_route_table: vpc_id: "{{ _vpc_b.vpc.id }}" tags: Name: "PublicRoute" routes: - dest: "{{ vpc_a.cidr_blodk }}" vpc_peering_connection_id: "{{ _vpc_peer.peering_id }}" profile: "{{ profile }}" region: "{{ region }}" when: - _vpc_peer.peering_id is defined
ポイントとなるのは、ピアリングするVPCをそれぞれ作成し、register
で登録した変数からVPC IDを取得している点です。
また、Routeはピアリングが完了した後に作成しなければなりません。
なお、VPCのCIDRブロックが重複しているとピアリングは失敗します。
ec2_vpc_peer
ec2_vpc_peerモジュールは、VPC ピアリングを作成します。
- name: "VPC Peering" ec2_vpc_peer: vpc_id: "{{ _vpc_a.vpc.id }}" peer_vpc_id: "{{ _vpc_b.vpc.id }}" profile: "{{ profile }}" region: "{{ region }}" register: _vpc_peer
ピアリング元のVPC IDと、ピアリング先のVPC IDを指定してください。 各VPC IDはec2_vpc_netモジュールの実行結果から取得できます。
続けてピアリングを承認します。
- name: "Accept VPC Peering request" ec2_vpc_peer: peering_id: "{{ _vpc_peer.peering_id }}" state: accept profile: "{{ profile }}" region: "{{ region }}" when: _vpc_peer.peering_id is defined
承認時は、peering_id
と、state
にaccept
を指定してください。
when
句でpeering_id
の存在チェックを行っているのは、ec2_vpc_peerがcheck
モードに対応していないためです。
check
モードの時に変数参照に失敗し、エラーになることを回避しています。
このあたりは今後のアップデートで改善するでしょう。
注意しなければならないのはec2_vpc_peerモジュールでは、作成と承認を別のタスクとして定義しなければならない点です。 これは違和感を感じるかも知れませんが、AWSアカウントを横断したピアリングの例を見ると納得できると思います。
ルーティング
VPC ピアリングをしただけでは通信することができません。 ec2_vpc_peerモジュールを使い、ルーティングを設定しましょう。
- name: Public Route VPC A ec2_vpc_route_table: vpc_id: "{{ _vpc_a.vpc.id }}" tags: Name: "PublicRoute" routes: - dest: "{{ vpc_b.cidr_blodk }}" vpc_peering_connection_id: "{{ _vpc_peer.peering_id }}" profile: "{{ profile }}" region: "{{ region }}" when: - _vpc_peer.peering_id is defined - name: Public Route VPC B ec2_vpc_route_table: vpc_id: "{{ _vpc_b.vpc.id }}" tags: Name: "PublicRoute" routes: - dest: "{{ vpc_a.cidr_blodk }}" vpc_peering_connection_id: "{{ _vpc_peer.peering_id }}" profile: "{{ profile }}" region: "{{ region }}" when: - _vpc_peer.peering_id is defined
ポイントとなるのは、routes
でvpc_peering_connection_id
の指定時に、register
で登録されたPEERING IDを利用している点です。
また、dest
には通信先となるVPCのCIDRブロックを指定することになるため、グループ変数をそのまま利用しています。
このように変数を参照することで、よりプログラマチックにPlaybookを書くことができます。
AWSアカウントを横断したVPC Peering
Playbookです。
--- - hosts: localhost connection: local gather_facts: False vars: profile_a: account_a profile_b: account_b aws_account_id_b: "999999999999" region: us-east-2 vpc_a: name: VPC A cidr_blodk: 10.0.0.0/16 vpc_b: name: VPC B cidr_blodk: 10.1.0.0/16 tasks: - name: "{{ vpc_a.name }}" ec2_vpc_net: name: "{{ vpc_a.name }}" cidr_block: "{{ vpc_a.cidr_blodk }}" profile: "{{ profile_a }}" region: "{{ region }}" register: _vpc_a - debug: var=_vpc_a verbosity=1 - name: "{{ vpc_b.name }}" ec2_vpc_net: name: "{{ vpc_b.name }}" cidr_block: "{{ vpc_b.cidr_blodk }}" profile: "{{ profile_b }}" region: "{{ region }}" register: _vpc_b - debug: var=_vpc_b verbosity=1 # VPC Peering - name: "VPC Peering" ec2_vpc_peer: vpc_id: "{{ _vpc_a.vpc.id }}" peer_owner_id: "{{ aws_account_id_b }}" peer_vpc_id: "{{ _vpc_b.vpc.id }}" profile: "{{ profile_a }}" region: "{{ region }}" register: _vpc_peer - debug: var=_vpc_peer verbosity=1 - name: "Accept VPC Peering request" ec2_vpc_peer: peering_id: "{{ _vpc_peer.peering_id }}" state: accept profile: "{{ profile_b }}" region: "{{ region }}" when: _vpc_peer.peering_id is defined - name: Public Route VPC A ec2_vpc_route_table: vpc_id: "{{ _vpc_a.vpc.id }}" tags: Name: "PublicRoute" routes: - dest: "{{ vpc_b.cidr_blodk }}" vpc_peering_connection_id: "{{ _vpc_peer.peering_id }}" profile: "{{ profile_a }}" region: "{{ region }}" when: - _vpc_peer.peering_id is defined - name: Public Route VPC B ec2_vpc_route_table: vpc_id: "{{ _vpc_b.vpc.id }}" tags: Name: "PublicRoute" routes: - dest: "{{ vpc_a.cidr_blodk }}" vpc_peering_connection_id: "{{ _vpc_peer.peering_id }}" profile: "{{ profile_b }}" region: "{{ region }}" when: - _vpc_peer.peering_id is defined
VPC AとVPC Bがそれぞれ別のAWSアカウントに作るため、各AWSアカウントのプロファイルが設定されます。
ピアリング作成時にpeer_owner_id
を指定しなければなりません。
また、ピアリング承認はピアリング先のAWSアカウントで行うため、プロファイルの指定が変わってくる点を注意しましょう。
まとめ
ec2_vpc_peerモジュールを利用して、VPCピアリングを作成しました。
WSリソースを構成管理する場合、今回の複数アカウント間のVPCピアリングのような、複数のAWSアカウントに横断するケースがあります。
Ansibleの場合、各モジュールのprofile
を指定し、順を追った手順を記述すれば、良いことになります。
このように、AWS CLIで構築スクリプトを書くことに近いでしょう。 構築スクリプトよりも宣言的でメンテしやすいのがAnsibleでAWSリソースの構成管理を行うメリットです。